package com.seelyn.rhea.mvc.internal;

import com.seelyn.rhea.mvc.common.exception.RheaException;
import com.seelyn.rhea.mvc.context.Request;
import com.seelyn.rhea.mvc.context.Response;
import com.seelyn.rhea.mvc.render.Render;
import com.seelyn.rhea.mvc.render.provider.JsonView;
import com.seelyn.rhea.mvc.render.provider.JspView;
import com.seelyn.rhea.mvc.render.provider.RedirectView;
import com.seelyn.rhea.mvc.route.FilterMatching;
import com.seelyn.rhea.mvc.route.HttpDispatcher;
import com.seelyn.rhea.mvc.route.Matching;
import com.seelyn.rhea.mvc.route.RouteMatching;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * @author slacrey
 * @since 2016/1/20
 */
public final class RheaDispatcher implements HttpDispatcher, Serializable {
    private static final long serialVersionUID = -8233432565022488662L;

    private static final Logger LOG = LoggerFactory.getLogger(RheaDispatcher.class);
    private final RheaFactory factory = RheaFactory.getInstance();

    private static final RheaDispatcher ourInstance = new RheaDispatcher();

    public static HttpDispatcher getInstance() {
        return ourInstance;
    }

    private RheaDispatcher() {

    }

    @Override @SuppressWarnings("unchecked")
    public boolean dispatch(Request request, Response response) throws RheaException {


        long startTime = System.nanoTime();
        boolean filterFlag = true;
        List<FilterMatching> beforeFilters = factory.query().getBeforeFilters(request);
        for (FilterMatching matching : beforeFilters) {

            appendUrlParams2Request(request, matching);
            filterFlag = matching.handle(request, response);
            if (!filterFlag) {
                break;
            }
        }
        long endTime = System.nanoTime();

        LOG.warn("时间:{}", endTime - startTime);

        if (!filterFlag) {
            return false;
        }
        long start = System.nanoTime();
        RouteMatching route = factory.query().getRoute(request);
        if (route != null) {

            appendUrlParams2Request(request, route);


            Object result = route.handle(request, response);

            try {
                if (result != null) {
                    if (result instanceof JsonView) {
                        JsonView jsonResult = (JsonView) result;
                        Render<JsonView> instance = jsonResult.getRender();
                        if (instance != null) {
                            instance.render(jsonResult, request, response);
                        }
                    } else if (result instanceof JspView) {
                        JspView jspResult = (JspView) result;
                        Render<JspView> instance = jspResult.getRender();
                        if (instance != null) {
                            instance.render(jspResult, request, response);
                        }
                    } else if (result instanceof RedirectView) {
                        RedirectView redirectResult = (RedirectView) result;
                        Render<RedirectView> instance = redirectResult.getRender();
                        if (instance != null) {
                            instance.render(redirectResult, request, response);
                        }
                    }
                } else {
                    throw new RheaException("route result is null.");
                }
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
                throw new RheaException(e.getMessage());
            }

            long end = System.nanoTime();
            LOG.warn("时间:{}", end - start);

            return true;
        } else {
            return false;
        }



    }

    /**
     * 获取url地址中的参数 .eg path=/test/:tes url:/test/1223
     * @param request 上下文对象 {@link Request}
     * @param route 路由匹配 {@link RouteMatching}
     */
    private boolean appendUrlParams2Request(Request request, Matching route) {

        Map<String, String> pathParams = route.getPathParams(request.uri());
        if (pathParams != null && !pathParams.isEmpty()) {
            for (Map.Entry<String, String> entry : pathParams.entrySet()) {
                request.params(entry.getKey(), entry.getValue());
            }
        }
        return true;
    }

}
